import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, SelectQueryBuilder, In } from 'typeorm';
import { Blog, Tag, User } from '@app/entities';
import {
  CreateBlogDto,
  UpdateBlogDto,
  QueryBlogsDto,
  BatchBlogActionDto
} from '../dto';

@Injectable()
export class BlogsService {
  constructor(
    @InjectRepository(Blog)
    private readonly blogRepository: Repository<Blog>,
    @InjectRepository(Tag)
    private readonly tagRepository: Repository<Tag>,
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) { }

  private readonly logger = new Logger(BlogsService.name);

  async findAll(query: QueryBlogsDto) {
    const queryBuilder = this.createBaseQuery();
    queryBuilder.andWhere('blog.isDeleted = :isDeleted', { isDeleted: false });

    // 搜索条件
    if (query.search) {
      queryBuilder.andWhere(
        '(blog.title LIKE :search OR blog.content LIKE :search OR blog.summary LIKE :search)',
        { search: `%${query.search}%` }
      );
    }

    // 分类筛选
    if (query.category) {
      queryBuilder.andWhere('blog.category = :category', { category: query.category });
    }

    // 状态筛选
    if (query.status !== undefined) {
      queryBuilder.andWhere('blog.status = :status', { status: query.status });
    }

    // 作者筛选
    if (query.authorId) {
      queryBuilder.andWhere('blog.authorId = :authorId', { authorId: query.authorId });
    }

    // 标签筛选
    if (query.tagId) {
      queryBuilder.andWhere('tags.id = :tagId', { tagId: query.tagId });
    }

    // 置顶筛选
    if (query.isTop !== undefined) {
      queryBuilder.andWhere('blog.isTop = :isTop', { isTop: query.isTop });
    }

    // 排序
    const sortBy = query.sortBy || 'createdAt';
    const sortOrder = query.sortOrder || 'DESC';

    // 置顶文章优先
    queryBuilder.orderBy('blog.isTop', 'DESC');
    queryBuilder.addOrderBy(`blog.${sortBy}`, sortOrder);

    // 分页
    const page = query.page || 1;
    const limit = query.limit || 10;
    const skip = (page - 1) * limit;

    queryBuilder.skip(skip).take(limit);

    const [blogs, total] = await queryBuilder.getManyAndCount();

    return {
      blogs: blogs.map(blog => this.formatBlogResponse(blog)),
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  async findOne(id: number): Promise<Blog> {
    const blog = await this.blogRepository.findOne({
      where: { id, isDeleted: false },
      relations: ['tags', 'authorUser'],
    });

    if (!blog) {
      throw new NotFoundException('博客不存在');
    }

    return blog;
  }

  async create(createBlogDto: CreateBlogDto): Promise<Blog> {
    // 验证作者是否存在
    const author = await this.userRepository.findOne({
      where: { id: createBlogDto.authorId },
    });

    if (!author) {
      throw new BadRequestException('作者不存在');
    }

    // 处理标签
    let tags = [];
    if (createBlogDto.tagIds && createBlogDto.tagIds.length > 0) {
      tags = await this.tagRepository.find({
        where: { id: In(createBlogDto.tagIds) },
      });

      if (tags.length !== createBlogDto.tagIds.length) {
        throw new BadRequestException('部分标签不存在');
      }
    }

    // 生成 slug（如果没有提供）
    if (!createBlogDto.slug) {
      createBlogDto.slug = this.generateSlug(createBlogDto.title);
    }

    // 计算阅读时间（如果没有提供）
    if (!createBlogDto.readingTime) {
      createBlogDto.readingTime = this.calculateReadingTime(createBlogDto.content);
    }

    // 创建博客
    const blog = this.blogRepository.create({
      ...createBlogDto,
      tags,
      publishedAt: createBlogDto.status === 1 ? new Date() : null,
    });

    const savedBlog = await this.blogRepository.save(blog);

    // 更新标签使用计数
    if (tags.length > 0) {
      await this.updateTagCounts(tags.map(tag => tag.id));
    }

    // 持久化 Markdown 文件（失败不影响创建）
    // try {
    //   await this.saveMarkdownToLocal(savedBlog);
    // } catch (err) {
    //   this.logger.warn(`保存 Markdown 失败: ${err?.message || err}`);
    // }

    return savedBlog;
  }

  async update(id: number, updateBlogDto: UpdateBlogDto): Promise<Blog> {
    const blog = await this.findOne(id);

    // 处理标签更新
    if (updateBlogDto.tagIds !== undefined) {
      let tags = [];
      if (updateBlogDto.tagIds.length > 0) {
        tags = await this.tagRepository.find({
          where: { id: In(updateBlogDto.tagIds) },
        });

        if (tags.length !== updateBlogDto.tagIds.length) {
          throw new BadRequestException('部分标签不存在');
        }
      }

      // 更新旧标签计数
      const oldTagIds = blog.tags.map(tag => tag.id);
      if (oldTagIds.length > 0) {
        await this.updateTagCounts(oldTagIds, -1);
      }

      blog.tags = tags;

      // 更新新标签计数
      if (tags.length > 0) {
        await this.updateTagCounts(tags.map(tag => tag.id));
      }
    }

    // 处理发布状态变化
    if (updateBlogDto.status !== undefined) {
      if (updateBlogDto.status === 1 && blog.status !== 1) {
        // 从未发布变为发布
        blog.publishedAt = new Date();
      } else if (updateBlogDto.status !== 1 && blog.status === 1) {
        // 从发布变为未发布
        blog.publishedAt = null;
      }
    }

    // 更新阅读时间
    if (updateBlogDto.content && updateBlogDto.content !== blog.content) {
      updateBlogDto.readingTime = this.calculateReadingTime(updateBlogDto.content);
    }

    Object.assign(blog, updateBlogDto);
    return await this.blogRepository.save(blog);
  }

  async remove(id: number): Promise<void> {
    const blog = await this.findOne(id);

    // 软删除：仅标记，不物理删除
    blog.isDeleted = true;
    await this.blogRepository.save(blog);
  }

  async batchAction(batchActionDto: BatchBlogActionDto): Promise<any> {
    const { blogIds, action } = batchActionDto;

    switch (action) {
      case 'publish':
        await this.blogRepository.update(blogIds, {
          status: 1,
          publishedAt: new Date()
        });
        break;
      case 'unpublish':
        await this.blogRepository.update(blogIds, {
          status: 0,
          publishedAt: null
        });
        break;
      case 'top':
        await this.blogRepository.update(blogIds, { isTop: true });
        break;
      case 'untop':
        await this.blogRepository.update(blogIds, { isTop: false });
        break;
      case 'delete':
        await this.blogRepository.update({ id: In(blogIds) }, { isDeleted: true });
        break;
      case 'changeCategory':
        if (batchActionDto.targetCategory) {
          await this.blogRepository.update(blogIds, {
            category: batchActionDto.targetCategory
          });
        }
        break;
      default:
        throw new BadRequestException('不支持的操作类型');
    }

    return {
      processed: blogIds.length,
      action,
    };
  }

  async publish(id: number): Promise<void> {
    await this.blogRepository.update({ id, isDeleted: false }, {
      status: 1,
      publishedAt: new Date()
    });
  }

  async unpublish(id: number): Promise<void> {
    await this.blogRepository.update({ id, isDeleted: false }, {
      status: 0,
      publishedAt: null
    });
  }

  async setTop(id: number): Promise<void> {
    await this.blogRepository.update({ id, isDeleted: false }, { isTop: true });
  }

  async removeTop(id: number): Promise<void> {
    await this.blogRepository.update({ id, isDeleted: false }, { isTop: false });
  }

  async restore(id: number): Promise<void> {
    const blog = await this.blogRepository.findOne({ where: { id, isDeleted: true } });
    if (!blog) {
      throw new NotFoundException('博客不存在或未被删除');
    }
    blog.isDeleted = false;
    await this.blogRepository.save(blog);
  }

  async getStats(): Promise<any> {
    const totalBlogs = await this.blogRepository.count();
    const publishedBlogs = await this.blogRepository.count({ where: { status: 1 } });
    const draftBlogs = await this.blogRepository.count({ where: { status: 0 } });
    const topBlogs = await this.blogRepository.count({ where: { isTop: true } });

    // 按分类统计
    const categoryStats = await this.blogRepository
      .createQueryBuilder('blog')
      .select('blog.category, COUNT(*) as count')
      .groupBy('blog.category')
      .getRawMany();

    // 最受欢迎的博客
    const popularBlogs = await this.blogRepository.find({
      where: { status: 1 },
      order: { viewCount: 'DESC' },
      take: 10,
      relations: ['tags'],
    });

    return {
      totalBlogs,
      publishedBlogs,
      draftBlogs,
      topBlogs,
      categoryStats,
      popularBlogs: popularBlogs.map(blog => this.formatBlogResponse(blog)),
    };
  }

  private createBaseQuery(): SelectQueryBuilder<Blog> {
    return this.blogRepository
      .createQueryBuilder('blog')
      .leftJoinAndSelect('blog.tags', 'tags')
      .leftJoinAndSelect('blog.authorUser', 'authorUser');
  }

  private generateSlug(title: string): string {
    return title
      .toLowerCase()
      .replace(/[^\w\s-]/g, '')
      .replace(/\s+/g, '-')
      .trim();
  }

  private calculateReadingTime(content: string): number {
    const wordsPerMinute = 200;
    const textContent = content.replace(/<[^>]*>/g, '');
    const wordCount = textContent.split(/\s+/).length;
    return Math.ceil(wordCount / wordsPerMinute);
  }

  private async updateTagCounts(tagIds: number[], increment: number = 1): Promise<void> {
    for (const tagId of tagIds) {
      await this.tagRepository.increment({ id: tagId }, 'count', increment);
    }
  }

  private formatBlogResponse(blog: Blog): any {
    return {
      ...blog,
      tagNames: blog.tagNames,
      isPublished: blog.isPublished,
      preview: blog.preview,
      estimatedReadTime: blog.estimatedReadTime,
    };
  }
  /**
   * 将blog的原文写入本地的md文件中
   * @param blog Blog
   */

  private async saveMarkdownToLocal(blog: Blog): Promise<void> {
    const path = await import('path');
    const fs = await import('fs');
    const fsp = await import('fs/promises');

    const contentDir = path.resolve(process.cwd(), 'apps', 'web-blog', 'content', 'blog');
    await fsp.mkdir(contentDir, { recursive: true });

    const effectiveSlug = (blog.slug && blog.slug.trim().length > 0)
      ? blog.slug.trim()
      : this.generateSlug(blog.title);

    const baseName = `${blog.id}-${effectiveSlug}`;
    let fileName = `${baseName}.md`;
    let attempt = 1;
    while (true) {
      try {
        await fsp.access(path.join(contentDir, fileName));
        // 文件存在，尝试下一个序号
        fileName = `${baseName}-${attempt}.md`;
        attempt++;
      } catch {
        // 文件不存在，可用
        break;
      }
    }

    const dateSource: Date = blog.publishedAt ?? blog.createdAt ?? new Date();
    const dateStr = this.formatDateYYYYMMDD(dateSource);
    const minRead = blog.readingTime ?? this.calculateReadingTime(blog.content);
    const authorName = blog.author ?? blog.authorUser?.nickName ?? blog.authorUser?.username ?? '';
    const authorAvatar = blog.authorUser?.avatar ?? '';
    const tags = Array.isArray(blog.tags) ? blog.tags.map((t: any) => t.name || t) : [];

    const yamlString = this.buildFrontmatter({
      title: blog.title,
      description: blog.summary || '',
      date: dateStr,
      image: blog.cover || '',
      minRead,
      author: {
        name: authorName,
        avatar: { src: authorAvatar, alt: authorName },
      },
      type: blog.category,
      tags,
      category: blog.category,
      status: blog.status,
      isTop: !!blog.isTop,
      slug: effectiveSlug,
    });

    const fileContent = `${yamlString}\n\n${blog.content || ''}`;
    const targetPath = path.join(contentDir, fileName);
    await fsp.writeFile(targetPath, fileContent, { encoding: 'utf-8' });
  }

  private buildFrontmatter(data: any): string {
    const esc = (v: string) => JSON.stringify(v ?? '');
    const arr = (items: any[]) => (items || []).map(i => `  - ${esc(String(i))}`).join('\n');
    return [
      '---',
      `title: ${esc(data.title)}`,
      `description: ${esc(data.description)}`,
      `date: ${esc(data.date)}`,
      `image: ${esc(data.image)}`,
      `minRead: ${Number(data.minRead) || 0}`,
      'author:',
      `  name: ${esc(data.author?.name || '')}`,
      '  avatar:',
      `    src: ${esc(data.author?.avatar?.src || '')}`,
      `    alt: ${esc(data.author?.avatar?.alt || '')}`,
      `type: ${esc(data.type || '')}`,
      'tags:',
      arr(data.tags || []),
      `category: ${esc(data.category || '')}`,
      `status: ${Number(data.status) || 0}`,
      `isTop: ${data.isTop ? 'true' : 'false'}`,
      `slug: ${esc(data.slug || '')}`,
      '---',
    ].join('\n');
  }

  private formatDateYYYYMMDD(d: Date): string {
    const yyyy = d.getFullYear();
    const mm = String(d.getMonth() + 1).padStart(2, '0');
    const dd = String(d.getDate()).padStart(2, '0');
    return `${yyyy}-${mm}-${dd}`;
  }
}
